/**
 * 
 */
package co.fitstart.coach.service.impl;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.demo2do.core.BusinessException;
import com.demo2do.core.persistence.GenericDaoSupport;
import com.demo2do.core.persistence.annotation.Dynamic;
import com.demo2do.core.utils.DateFormatUtils;
import co.fitstart.coach.dao.CoachDao;
import co.fitstart.coach.dao.CourseDao;
import co.fitstart.coach.dao.RecordDao;
import co.fitstart.coach.dao.SalaryDao;
import co.fitstart.coach.dao.StudentDao;
import co.fitstart.coach.service.SigninService;
import co.fitstart.entity.Constant;
import co.fitstart.entity.coach.CoachSigninRecord;
import co.fitstart.entity.coach.CoachSigninStatistic;
import co.fitstart.entity.coach.CoachStudentRelation;
import co.fitstart.entity.course.round.CourseRound;
import co.fitstart.entity.course.round.CourseRoundResult;
import co.fitstart.entity.course.schedule.CourseSchedule;
import co.fitstart.entity.course.schedule.CourseScheduleAttribute;
import co.fitstart.entity.course.schedule.ScheduleStatus;
import co.fitstart.entity.course.schedule.SigninRecord;
import co.fitstart.entity.order.Order;
import co.fitstart.entity.order.OrderStatus;
import co.fitstart.entity.principal.Principal;
import co.fitstart.entity.user.bwh.BwhRecord;
import co.fitstart.entity.user.inbody.InbodyRecord;

/**
 * @author downpour
 *
 */
@Service("signinService")
public class SigninServiceImpl implements SigninService {

	private static final Logger logger = LoggerFactory.getLogger(SigninService.class);
	
    @Autowired
    private GenericDaoSupport genericDaoSupport;
    
    @Dynamic
    private SalaryDao salaryDao;
    
    @Dynamic
    private CourseDao courseDao;
    
    @Dynamic
    private StudentDao studentDao;
    
    @Dynamic
    private CoachDao coachDao;
    
    @Dynamic
    private RecordDao recordDao;
    
    /* 
     * (non-Javadoc)
     * @see co.fitstart.coach.service.SigninService#signin(co.fitstart.entity.principal.Principal, co.fitstart.entity.course.schedule.CourseSchedule, co.fitstart.entity.user.bwh.BwhRecord, java.lang.String, boolean, int)
     */
    public void signin(Principal coach, CourseSchedule courseSchedule, BwhRecord bwhRecord, String comment, boolean adminOperation, int finished) {
        
        logger.info("#signin() with params courseScheduleId [{}], coachId[{}], studentId [{}]", courseSchedule.getId(), courseSchedule.getCoach().getId(), courseSchedule.getStudent().getId());
        
        // check whether this is an admin operation and check whether course schedule could be signin
        if(!adminOperation && !courseSchedule.couldSignin()) {
            throw new BusinessException("您已不在签到时限内，请后台手动签到");
        }
        
        // check scheduleStatus of this courseSchedule in order to avoid multiple-time sign in
        if(courseSchedule.getScheduleStatus().isFinish()){
            logger.error("#signin() with params courseScheduleId[{}], coachId[{}], studentId[{}], siteId[{}], 无法重复签到", courseSchedule.getId(), courseSchedule.getCoach().getId(), courseSchedule.getStudent().getId(), courseSchedule.getSite().getId());
            throw new BusinessException("无法重复签到");
        }
        
        // 1. check dishonesty
        CourseRound round = courseSchedule.getCourseRound();
        if(round != null && round.isDishonesty()) {
            List<ScheduleStatus> scheduleStatus = new ArrayList<>();
            scheduleStatus.add(ScheduleStatus.FINISH);
            scheduleStatus.add(ScheduleStatus.EXPIRY);
            
            List<CourseSchedule> schedules = courseDao.listCourseSchedule(round, scheduleStatus, 0, 1);
            if(schedules == null || schedules.size() == 0 
                    || schedules.get(0).getScheduleStatus() == ScheduleStatus.FINISH) {
                genericDaoSupport.update(round.editAttribute("dishonesty", "0"));
            }
        }
        
        // 2. sign in for course schedule
        genericDaoSupport.update(courseSchedule.signin(comment));
        
        // 3. create course schedule attribute
        this.createCourseScheduleAttr(courseSchedule, coach);
        
        // 4. save or update coach student relation
        this.setupCoachStudentRelation(coach, courseSchedule);
        
        // 5. check course round finish status & do finish things
        CourseRound courseRound = this.checkCourseRoundStatus(coach, courseSchedule);
        
        // 6. update order status when course round finished
        this.checkOrderStatus(courseRound);
        
        // 7. save signin record
        this.createSigninRecord(courseSchedule);
        
        // 8. save bwhRecord
        if(courseSchedule.getCourseRound().isFullInTarget()) {
            logger.trace("SigninServiceImpl#signin() - courseScheduleId [{}], fullInTarget true.", courseSchedule.getId());
            if((finished == 0 || finished % 4 == 3) && !courseSchedule.getCourse().isExperience()) {
                if(bwhRecord.getBust() == null || bwhRecord.getWaist() == null || bwhRecord.getHips() == null) {
                    throw new BusinessException("三围数据不能为空");
                }
                genericDaoSupport.save(bwhRecord.initilization(courseSchedule.getCourseRound(), coach));
            }
        }
        
        // 9. save coach signin record and update coach signin statistic
        this.createCoachSigninRecord(courseSchedule);
    }
    
    /**
     * 
     * @param courseSchedule
     * @param coach
     */
    private void createCourseScheduleAttr(CourseSchedule courseSchedule, Principal coach) {
        
        // create signinPersonName attribute
        CourseScheduleAttribute signinPersonName = new CourseScheduleAttribute(courseSchedule, Constant.SIGNIN_PERSON_NAME, coach.getName());
        genericDaoSupport.save(signinPersonName);
        
        // create signinPersonId attribute
        CourseScheduleAttribute signPersonId = new CourseScheduleAttribute(courseSchedule, Constant.SIGN_PERSON_ID, coach.getIdString());
        genericDaoSupport.save(signPersonId);
        
        // create signinTime attribute
        CourseScheduleAttribute signinTime = new CourseScheduleAttribute(courseSchedule, Constant.SIGNIN_TIME, DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
        genericDaoSupport.save(signinTime);
    }
    
    /**
     * 
     * @param coach
     * @param courseSchedule
     */
    private void setupCoachStudentRelation(Principal coach, CourseSchedule courseSchedule) {
        
        CoachStudentRelation coachStudentRelation = studentDao.loadCoachStudentRelation(coach, courseSchedule.getCourseType(), courseSchedule.getStudent());
        
        if(coachStudentRelation == null) {
            coachStudentRelation = new CoachStudentRelation(coach, courseSchedule);
            genericDaoSupport.save(coachStudentRelation);
        }else {
            genericDaoSupport.update(coachStudentRelation.updateTime());
        }
    }
    
    /**
     * 
     * @param coach
     * @param courseSchedule
     * @return
     */
    private CourseRound checkCourseRoundStatus(Principal coach, CourseSchedule courseSchedule) {
        
        // do sign in course round
        CourseRound courseRound = courseSchedule.getCourseRound();
        genericDaoSupport.update(courseRound.signin());
        
        if(courseRound.getCourseType().isIndividual()) {
            int courseAmount = courseRound.getCourseAmount();
            
            // count finished courseSchedule 
            int finishedSchedule = courseDao.countFinishedSchedule(courseRound, ScheduleStatus.FINISH);
            int allScheduleCount = courseDao.countAllSchedule(courseRound);
            
            // if course round finish count equal finished schedule and finished schedule is all schedule
            if(courseAmount == finishedSchedule && allScheduleCount == finishedSchedule) {
                genericDaoSupport.update(courseRound.finishCourseRound());
                
                //1V1结营，记录初始与结营时三围和inbody
                if(!courseRound.getCourse().isExperience()) {
                    List<CourseSchedule> courseSchedules = courseDao.listCourseSchedule(courseRound);
                    
                    if(!courseSchedules.isEmpty()) {
                        Date begin = courseSchedules.get(courseSchedules.size() - 1).getOccurDate();
                        Date end = courseSchedules.get(0).getOccurDate();
                        
                        List<BwhRecord> bwhRecords = recordDao.listBwhRecord(courseRound.getStudent().getUser(), DateFormatUtils.format(begin, "yyyy-MM-dd") + " 00:00:00", DateFormatUtils.format(end, "yyyy-MM-dd") + " 23:59:59");
                        
                        List<InbodyRecord> inbodyRecords = recordDao.listInbodyRecord(courseRound.getStudent().getUser(), DateFormatUtils.format(begin, "yyyy-MM-dd") + " 00:00:00", DateFormatUtils.format(end, "yyyy-MM-dd") + " 23:59:59");
                        
                        if(!bwhRecords.isEmpty() || !inbodyRecords.isEmpty()) {
                            BwhRecord previousBwh = null;
                            BwhRecord laterBwh = null;
                            InbodyRecord previousInbody = null;
                            InbodyRecord laterInbody = null;
                            
                            if(!bwhRecords.isEmpty()) {
                                previousBwh = bwhRecords.get(bwhRecords.size() - 1);
                                laterBwh = bwhRecords.get(0);
                            }
                            
                            if(!inbodyRecords.isEmpty()) {
                                previousInbody = inbodyRecords.get(inbodyRecords.size() - 1);
                                laterInbody = inbodyRecords.get(0);
                            }
                            
                            CourseRoundResult result = new CourseRoundResult(courseRound, previousBwh, laterBwh, previousInbody, laterInbody);
                            genericDaoSupport.save(result);
                        }
                    }
                }
            }
        }
        
        
        
        return courseRound;
    
    }
    
   /**
    * 
    * @param courseRound
    */
    private void checkOrderStatus(CourseRound courseRound) {
        
        if(courseRound.isFinish()) {
            
            Order order = courseRound.getOrder();
            
            if(order != null) {
                order.orderStatus(OrderStatus.COMPLETE);
                genericDaoSupport.update(order);
            }
            
        }
    }
    
    /**
     * 
     * @param courseSchedule
     */
    private void createSigninRecord(CourseSchedule courseSchedule) {
        SigninRecord signinRecord = new SigninRecord(courseSchedule, true);
        genericDaoSupport.save(signinRecord);
    }
    
    /**
     * 
     * @param courseSchedule
     */
    private void createCoachSigninRecord(CourseSchedule courseSchedule) {
        // save coach signin record first
        CoachSigninRecord signinRecord = new CoachSigninRecord(courseSchedule);
        genericDaoSupport.save(signinRecord);
        
        if(!courseSchedule.getCourse().isExperience() || courseSchedule.getStudent().getUser().isOrdered()) {
            // save or update this month has coach signin statistic
            CoachSigninStatistic signinStatistic = coachDao.loadCoachSigninStatistic(courseSchedule.getCoach(), DateFormatUtils.format(courseSchedule.getOccurDate(), "yyyy-MM"));
            if(signinStatistic == null) {
                signinStatistic = new CoachSigninStatistic(courseSchedule);
                genericDaoSupport.save(signinStatistic);
            } else {
                genericDaoSupport.update(signinStatistic.hour(courseSchedule.getCourse().isExperience() ? 1.5 : 1));
            }
        }
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.SigninService#getCoachHour(co.fitstart.entity.principal.Principal)
     */
    public double getCoachHour(Principal coach) {
        BigDecimal hour = coachDao.getCoachHour(coach);
        return hour == null ? 0 : hour.doubleValue();
    }

}
